
/*
    Software License

    Copyright (C) 2021-05-24  Xoronos

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, version 3.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

/*
    Liabilities

    The software is provided "AS IS" without any warranty of any kind, either expressed,
    implied, or statutory, including, but not limited to, any warranty that the software
    will conform to specifications, any implied warranties of merchantability, fitness
    for a particular purpose, and freedom from infringement, and any warranty that the
    documentation will conform to the software, or any warranty that the software will
    be error free.

    In no event shall Xoronos be liable for any damages, including, but not limited to,
    direct, indirect, special or consequential damages, arising out of, resulting from,
    or in any way connected with this software, whether or not based upon warranty,
    contract, tort, or otherwise, whether or not injury was sustained by persons or
    property or otherwise, and whether or not loss was sustained from, or arose out of
    the results of, or use of, the software or services provided hereunder.

    To request the provided software under a different license you can contact us at
    support@xoronos.com
*/

#include "confgen.h"

int permutation_compilation_settings( uint64_t permutation_rnd[4]
 )
{

    char buffer1[32];
    char buffer2[32];
    char buffer3[32];
    char buffer4[32];
    char buffer[XRN_MAX_BUFF_SIZE_CONFGEN];
    int i, j;

    int reloop;

    reloop = 0;

    while ( reloop == 0 ) {

        reloop = 1;
        printf( "for the permutation settings type the action (generate/restore): " );

        if ( fgets( buffer, XRN_MAX_BUFF_SIZE_CONFGEN, stdin ) == NULL ) {
            printf( "input output error \n" );
        }

        if ( !strncmp( buffer, "generate", strlen( "generate" ) ) ) {
            printf( "\nour recomandation for new users: random\n" );
            printf( "to generate the permutation settings type the mode (random/default): " );

            if ( fgets( buffer, XRN_MAX_BUFF_SIZE_CONFGEN, stdin ) == NULL ) {
                printf( "input output error \n" );
            }

            if ( !strncmp( buffer, "random", strlen( "random" ) ) ) {
                if ( get_random_bytes( ( uint8_t * ) & permutation_rnd[0], sizeof( uint64_t ) * 4 ) !=
                     XSUCCESS ) {
                    printf( "io error\n" );
                    return XERR_IO;
                }

                printf( "random configuration successfully generated.\n" );
                printf
                    ( "to regenerate the permutation configuration in a future compilation, copy the following key.\n" );

                base64_encode( ( char * ) &permutation_rnd[0], sizeof( uint64_t ) * 4, buffer );
                prettify_b64( buffer );

                printf( "\npermutation key: " );
                for ( j = 0; j < strlen( buffer ) / 4; j++ ) {
                    printf( "%c", buffer[j] );
                }

                printf( " " );
                for ( j = ( uint8_t ) ( strlen( buffer ) / 4 ); j < strlen( buffer ) / 2; j++ ) {
                    printf( "%c", buffer[j] );
                }

                printf( " " );
                for ( j = ( uint8_t ) ( strlen( buffer ) / 2 ); j < 3 * strlen( buffer ) / 4; j++ ) {
                    printf( "%c", buffer[j] );
                }

                printf( " " );
                for ( j = ( uint8_t ) ( 3 * strlen( buffer ) / 4 ); j < strlen( buffer ); j++ ) {
                    printf( "%c", buffer[j] );
                }

                printf( "\n\n" );

            } else if ( !strncmp( buffer, "default", strlen( "default" ) ) ) {
                printf( "default configuration selected.\n" );
            } else {
                printf( "option not understood.\n" );
                reloop = 0;
            }

        } else if ( !strncmp( buffer, "restore", strlen( "restore" ) ) ) {

            printf( "permutation key : " );
            if ( fgets( buffer, XRN_MAX_BUFF_SIZE_CONFGEN, stdin ) == NULL ) {
                printf( "input output error \n" );
            }

            sscanf( buffer, "%s %s %s %s", buffer1, buffer2, buffer3, buffer4 );
            printf( "\n" );

            for ( i = 0; i < 11; i++ ) {
                buffer[0 * 11 + i] = buffer1[i];
                buffer[1 * 11 + i] = buffer2[i];
                buffer[2 * 11 + i] = buffer3[i];
                buffer[3 * 11 + i] = buffer4[i];
            }

            if ( ( strlen( buffer1 ) != 11 ) || ( strlen( buffer2 ) != 11 ) || ( strlen( buffer3 ) != 11 )
                 || ( strlen( buffer4 ) != 11 ) ) {
                printf( "option not understood.\n" );
                reloop = 0;
            }

            buffer[44] = '\0';
            deprettify_b64( buffer );
            if ( base64_decode( buffer, ( int ) strlen( buffer ), ( char * ) &permutation_rnd[0] ) !=
                 XSUCCESS ) {
                printf( "wrong formatted key.\n" );
                reloop = 0;
            } else {

                printf( "permutation key restored.\n" );

            }

        } else {

            printf( "option not understood.\n" );
            reloop = 0;

        }

    }

    return XSUCCESS;

}

int print_permutation_compilation_settings( uint64_t permutation_rnd[4], FILE * permutation_settings_fp
 )
{

    int i, j;
    uint8_t rnd;
    uint8_t tmp;
    uint16_t rnd16;
    uint16_t tmp16;
    uint32_t magic_number;
    char buffer[XRN_MAX_BUFF_SIZE_CONFGEN];
    char buffer_out[XRN_MAX_BUFF_SIZE_CONFGEN];
    xrn_encoding_param_t encoding_param;
    xrn_encoding_tmp_param_t enc_tmp;

    uint8_t v256[256] =
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
        27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
        77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
        102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
        122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
        142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
        162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
        182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
        202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
        222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
        242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255
    };

    uint16_t v288[288] =
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
        27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
        77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
        102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
        122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141,
        142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
        162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181,
        182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201,
        202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221,
        222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
        242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261,
        262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281,
        282, 283, 284, 285, 286, 287
    };

    uint8_t v128[128] =
        { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
        27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
        77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101,
        102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
        122, 123, 124, 125, 126, 127
    };

    ///////////////////////////////
    // Generate permutation file //
    ///////////////////////////////

    for ( i = 0; i < sizeof( uint64_t ) * 4; i++ ) {
        buffer[i] = ( char ) ( ( ( uint8_t * ) & permutation_rnd[0] )[i] );
    }

    initilize_enc_param_confgen( &encoding_param );
    encoding_param.prob_checks.check_running_error_en = 0;
    encoding_param.prob_checks.check_disparity_error_en = 0;

    // shuffle key times
    for ( j = 0; j < sizeof( uint64_t ) * 4; j++ ) {
        xrn_encode( &encoding_param, ( uint8_t * ) buffer, ( uint8_t * ) buffer, sizeof( uint64_t ) * 4,
                    &enc_tmp );
    }

    for ( i = 255; i >= 0; i-- ) {
        buffer[i] = ( char ) i;
    }

    // shuffle 256 times
    for ( j = 0; j < 256; j++ ) {
        xrn_encode( &encoding_param, ( uint8_t * ) buffer, ( uint8_t * ) buffer_out, 256, &enc_tmp );
        for ( i = 255; i >= 0; i-- ) {
            rnd = ( uint8_t ) buffer[i];
            rnd = ( uint8_t ) ( rnd % ( i + 1 ) );
            tmp = v256[i];
            v256[i] = v256[rnd];
            v256[rnd] = tmp;
            buffer[i] = buffer_out[i];
        }

    }

    // shuffle 128 times
    for ( j = 0; j < 128; j++ ) {
        xrn_encode( &encoding_param, ( uint8_t * ) buffer, ( uint8_t * ) buffer, 256, &enc_tmp );
        for ( i = 127; i >= 0; i-- ) {
            rnd = ( uint8_t ) buffer[i];
            rnd = ( uint8_t ) ( rnd % ( i + 1 ) );
            tmp = v128[i];
            v128[i] = v128[rnd];
            v128[rnd] = tmp;
        }

    }

    // shuffle 288 times
    for ( j = 0; j < 288; j++ ) {
        xrn_encode( &encoding_param, ( uint8_t * ) buffer, ( uint8_t * ) buffer, 256, &enc_tmp );
        for ( i = 287; i >= 0; i-- ) {
            rnd16 = ( uint16_t ) ( buffer[i] * 256 + buffer[( i + 1 ) % XRN_MAX_BUFF_SIZE_CONFGEN] );
            rnd16 = ( uint16_t ) ( rnd16 % ( i + 1 ) );
            tmp16 = v288[i];
            v288[i] = v288[rnd16];
            v288[rnd16] = tmp16;
        }

    }

    // derive magic number
    magic_number = 0xffffffffu;
    for ( j = 0; j < 256 + 128; j++ ) {
        xrn_crc32( v256, 256, &magic_number );
        xrn_crc32( v128, 128, &magic_number );
        xrn_crc32( ( uint8_t * ) v288, 288 * 2, &magic_number );        // uint16_t
    }

    // print the results

    if ( fprintf
         ( permutation_settings_fp,
           "#ifndef XRN_PERMUTATION_SETTINGS_H\n#define XRN_PERMUTATION_SETTINGS_H\n#include <stdint.h>\n#if ( XNBITS == 256 )\n#define XRN_PERMUTATIONS {" )
         < 0 ) {
        printf( "io error\n" );
        return XERR_IO;
    }

    for ( i = 0; i < 256; i++ ) {
        if ( i != 255 ) {
            if ( fprintf( permutation_settings_fp, " %3u,", v256[i] ) < 0 ) {
                printf( "io error\n" );
                return XERR_IO;
            }

        } else {
            if ( fprintf
                 ( permutation_settings_fp, " %3u };\n#elif ( XNBITS == 128 )\n#define XRN_PERMUTATIONS {",
                   v256[i] ) < 0 ) {
                printf( "io error\n" );
                return XERR_IO;
            }

        }

    }

    for ( i = 0; i < 128; i++ ) {
        if ( i != 127 ) {
            if ( fprintf( permutation_settings_fp, " %3d,", v128[i] ) < 0 ) {
                printf( "io error\n" );
                return XERR_IO;
            }

        } else {
            if ( fprintf
                 ( permutation_settings_fp,
                   " %3d };\n#endif\n#define XRN_PERMUTATION_NUMBER UINT32_C(%u)\n#define XRN_ENCODING_PERMUTATION_LEN 288\n#define XRN_ENCODING_PERMUTATION { ",
                   v128[i], magic_number ) < 0 ) {
                printf( "io error\n" );
                return XERR_IO;
            }

        }

    }

    for ( i = 0; i < 288; i++ ) {
        if ( i != 287 ) {
            if ( fprintf( permutation_settings_fp, " %3d,", v288[i] ) < 0 ) {
                printf( "io error\n" );
                return XERR_IO;
            }

        } else {
            if ( fprintf( permutation_settings_fp, " %3d };\n ", v288[i] ) < 0 ) {
                printf( "io error\n" );
                return XERR_IO;
            }

        }

    }

    if ( fprintf( permutation_settings_fp, "#endif\n" ) < 0 ) {
        printf( "io error\n" );
        return XERR_IO;
    }

    return XSUCCESS;

}

